using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;

namespace Nais.Presentation.Web.Mvc
{
    /// <summary>
    /// 
    /// </summary>
    public class WrappedModelMetadata : ModelMetadata
    {
        private readonly ModelMetadata _innerMetadata;
        private readonly List<Func<ModelMetadata, ControllerContext, ModelValidator>> _validators = new List<Func<ModelMetadata, ControllerContext, ModelValidator>>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="innerMetadata"></param>
        public WrappedModelMetadata(ModelMetadataProvider provider, ModelMetadata innerMetadata)
            : base(provider, innerMetadata.ContainerType, () => innerMetadata.Model, innerMetadata.ModelType, innerMetadata.PropertyName)
        {
            _innerMetadata = innerMetadata;
            DataTypeName = innerMetadata.DataTypeName;
            TemplateHint = innerMetadata.TemplateHint;
        }

        /// <summary>
        /// Gets a dictionary that contains additional metadata about the model.
        /// </summary>
        public override Dictionary<string, object> AdditionalValues
        {
            get { return _innerMetadata.AdditionalValues; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether empty strings that are posted back in forms should be converted to null.
        /// </summary>
        /// <returns>
        /// true if empty strings that are posted back in forms should be converted to null; otherwise, false. The default value is true.
        /// </returns>
        public override bool ConvertEmptyStringToNull
        {
            get { return _innerMetadata.ConvertEmptyStringToNull; }
            set { _innerMetadata.ConvertEmptyStringToNull = value; }
        }

        /// <summary>
        /// Gets or sets meta information about the data type. 
        /// </summary>
        /// <returns>
        /// Meta information about the data type. 
        /// </returns>
        public override string DataTypeName
        {
            get { return _innerMetadata.DataTypeName; }
            set { _innerMetadata.DataTypeName = value; }
        }

        /// <summary>
        /// Gets or sets the description of the model.
        /// </summary>
        /// <returns>
        /// The description of the model. The default value is null.
        /// </returns>
        public override string Description
        {
            get { return _innerMetadata.Description; }
            set { _innerMetadata.Description = value; }
        }

        /// <summary>
        /// Gets or sets the display format string for the model.
        /// </summary>
        /// <returns>
        /// The display format string for the model.
        /// </returns>
        public override string DisplayFormatString
        {
            get { return _innerMetadata.DisplayFormatString; }
            set { _innerMetadata.DisplayFormatString = value; }
        }

        /// <summary>
        /// Gets or sets the edit format string of the model.
        /// </summary>
        /// <returns>
        /// The edit format string of the model.
        /// </returns>
        public override string EditFormatString
        {
            get { return _innerMetadata.EditFormatString; }
            set { _innerMetadata.EditFormatString = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the model object should be rendered using associated HTML elements.
        /// </summary>
        /// <returns>
        /// true if the associated HTML elements that contains the model object should be included with the object; otherwise, false.
        /// </returns>
        public override bool HideSurroundingHtml
        {
            get { return _innerMetadata.HideSurroundingHtml; }
            set { _innerMetadata.HideSurroundingHtml = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the model is read-only.
        /// </summary>
        /// <returns>
        /// true if the model is read-only; otherwise, false.
        /// </returns>
        public override bool IsReadOnly
        {
            get { return _innerMetadata.IsReadOnly; }
            set { _innerMetadata.IsReadOnly = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the model is required.
        /// </summary>
        /// <returns>
        /// true if the model is required; otherwise, false.
        /// </returns>
        public override bool IsRequired
        {
            get { return _innerMetadata.IsRequired; }
            set { _innerMetadata.IsRequired = value; }
        }

        /// <summary>
        /// Gets or sets the string to display for null values.
        /// </summary>
        /// <returns>
        /// The string to display for null values.
        /// </returns>
        public override string NullDisplayText
        {
            get { return _innerMetadata.NullDisplayText; }
            set { _innerMetadata.NullDisplayText = value; }
        }

        /// <summary>
        /// Gets or sets a value that represents order of the current metadata.
        /// </summary>
        /// <returns>
        /// The order value of the current metadata.
        /// </returns>
        public override int Order
        {
            get { return _innerMetadata.Order; }
            set { _innerMetadata.Order = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether request validation is enabled.
        /// </summary>
        /// <returns>
        /// true if request validation is enabled; otherwise, false.
        /// </returns>
        public override bool RequestValidationEnabled
        {
            get { return _innerMetadata.RequestValidationEnabled; }
            set { _innerMetadata.RequestValidationEnabled = value; }
        }

        /// <summary>
        /// Gets or sets a short display name.
        /// </summary>
        /// <returns>
        /// The short display name.
        /// </returns>
        public override string ShortDisplayName
        {
            get { return _innerMetadata.ShortDisplayName; }
            set { _innerMetadata.ShortDisplayName = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the property should be displayed in read-only views such as list and detail views.
        /// </summary>
        /// <returns>
        /// true if the model should be displayed in read-only views; otherwise, false.
        /// </returns>
        public override bool ShowForDisplay
        {
            get { return _innerMetadata.ShowForDisplay; }
            set { _innerMetadata.ShowForDisplay = value; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the model should be displayed in editable views.
        /// </summary>
        /// <returns>
        /// true if the model should be displayed in editable views; otherwise, false.
        /// </returns>
        public override bool ShowForEdit
        {
            get { return _innerMetadata.ShowForEdit; }
            set { _innerMetadata.ShowForEdit = value; }
        }

        /// <summary>
        /// Gets or sets the simple display string for the model.
        /// </summary>
        /// <returns>
        /// The simple display string for the model.
        /// </returns>
        public override string SimpleDisplayText
        {
            get { return _innerMetadata.SimpleDisplayText; }
            set { _innerMetadata.SimpleDisplayText = value; }
        }

        /// <summary>
        /// Gets or sets a hint that suggests what template to use for this model.
        /// </summary>
        /// <returns>
        /// A hint that suggests what template to use for this model.
        /// </returns>
        public override string TemplateHint
        {
            get { return _innerMetadata.TemplateHint; }
            set { _innerMetadata.TemplateHint = value; }
        }

        /// <summary>
        /// Gets or sets a value that can be used as a watermark.
        /// </summary>
        /// <returns>
        /// The watermark.
        /// </returns>
        public override string Watermark
        {
            get { return _innerMetadata.Watermark; }
            set { _innerMetadata.Watermark = value; }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="validatorFactory"></param>
        public void AddValidator(Func<ModelMetadata, ControllerContext, ModelValidator> validatorFactory)
        {
            _validators.Add(validatorFactory);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="validationAttribute"></param>
        public void AddValidator(ValidationAttribute validationAttribute)
        {
            AddValidator((m, c) => new DataAnnotationsModelValidator(m, c, validationAttribute));
        }

        /// <summary>
        /// Gets a list of validators for the model.
        /// </summary>
        /// <returns>
        /// A list of validators for the model.
        /// </returns>
        /// <param name="context">The controller context.</param>
        public override IEnumerable<ModelValidator> GetValidators(ControllerContext context)
        {
            return base.GetValidators(context)
                .Concat(_validators.Select(f => f(this, context)));
        }
    }
}